# ---------- 简介 ---------- #
# SuperFlow.py
# 在这个实现版本中, 主表中计数器的宽度为16位, 辅表中
# 摘要, 计数器和截图的宽度都为16字节, 并且我们在主表
# 中存放完整的流标识符
# 赵宗义, xinshengzzy@foxmail.com, 2020.12.15
# ------------------ #
from 标准库 import 累积序列
from simulators.BaseSimulator import BaseSimulator
import mytools
from 文件工具 import 备份数据
from simulators.Buckets import SFMain, SFAncillary
from simulators.事件 import 事件


class PromotionRecord:
    def __init__(self, 包序号, 索引, 标识符, 计数值):
        self.包序号 = 包序号
        self.索引 = 索引
        self.标识符 = 标识符
        self.计数值 = 计数值

    def getContent(self):
        return (self.包序号, self.索引, self.标识符, self.计数值)

    def getRecord(self):
        return (self.标识符, self.计数值)


class SuperFlow(BaseSimulator):
    def __init__(self, 内存=1.0, 协议="ipv4"):
        super(SuperFlow, self).__init__()
        self.内存 = 内存
        self.协议 = 协议
        self.导出数据量 = 0
        self.旧流导出次数 = 0
        self.小流导出次数 = 0
        tmp = int(内存*1024.0*1024.0/12.0)
        self.表长1 = int(tmp/3)
        self.表长2 = int(tmp/3)
        self.表长3 = int(tmp/3)
        self.表长4 = tmp
        self.主表 = []
        self.辅表 = []
        for i in range(self.表长1 + self.表长2 + self.表长3):
            self.主表.append(SFMain())
        for i in range(self.表长4):
            self.辅表.append(SFAncillary())
        self.标识符库 = dict()
        self.延迟 = 0

    def 设置延迟(self, 延迟):
        self.延迟 = 延迟

    def 提取延迟(self):
        return self.延迟

    def 登记标识符长度(self):
        if "ipv4" == self.协议:
            self.导出数据量 += 13
        elif "ipv6" == self.协议:
            self.导出数据量 += 37

    def 登记流记录长度(self):
        if "ipv4" == self.协议:
            self.导出数据量 += 21
        elif "ipv6" == self.协议:
            self.导出数据量 += 45

    def 处理事件(self, 临时事件):
        if 临时事件.数据包类型():
            self.处理数据包(临时事件)
        elif 临时事件.事务类型():
            self.处理事务(临时事件)
        else:
            raise SystemExit("不能识别事件类型.")

    def 处理数据包(self, 数据包):
        标识符 = 数据包.提取标识符()
        idx1, idx2, idx3, idx4 = self.计算索引(标识符)
        指纹 = self.计算指纹(标识符)

        mincnt = 0xffffffff
        minidx = -1
        maxcnt = 0
        maxidx = -1
        for idx in [idx1, idx2, idx3]:
            if self.主表[idx].empty():
                self.主表[idx].init(指纹, 1)
                self.标识符库[指纹] = 标识符
                self.登记标识符长度()
                return
            elif self.主表[idx].match(指纹):
                self.主表[idx].increase()
                return
            else:
                if self.主表[idx].getCnt() < mincnt:
                    mincnt = self.主表[idx].getCnt()
                    minidx = idx
                if self.主表[idx].getCnt() > maxcnt:
                    maxcnt = self.主表[idx].getCnt()
                    maxidx = idx

        摘要 = self.计算摘要(标识符)
        maxcnt = maxcnt & 0xffff
        if self.辅表[idx4].empty():
            self.辅表[idx4].init(摘要, 1, 1, maxcnt)
        elif self.辅表[idx4].match(摘要):
            self.辅表[idx4].increase()
            if self.辅表[idx4].getCnt() > mincnt:
                self.小流导出次数 += 1
                self.反馈(minidx, 标识符, self.辅表[idx4].getCnt(), 数据包.提取序号() + self.延迟)
                self.辅表[idx4].reset()
            elif self.辅表[idx4].getStatus() > maxcnt - self.辅表[idx4].getSnapshot() + 1:
                self.旧流导出次数 += 1
                self.反馈(maxidx, 标识符, self.辅表[idx4].getCnt(), 数据包.提取序号() + self.延迟)
                self.辅表[idx4].reset()
        elif self.辅表[idx4].getStatus() > 0:
            self.辅表[idx4].decrease()
        else:
            self.辅表[idx4].setDigest(摘要)
            self.辅表[idx4].setSnapshot(maxcnt)
            self.辅表[idx4].increase()

    def 处理事务(self, 临时事件):
        索引 = 临时事件.提取索引()
        指纹, 计数值 = self.主表[索引].getRecord()
        if 指纹 in self.标识符库:
            tmp = self.标识符库[指纹]
            del self.标识符库[指纹]
            self.导出流记录((tmp, 计数值))
            self.登记流记录长度()
        else:
            pass
        标识符 = 临时事件.提取标识符()
        计数值 = 临时事件.提取计数值()
        指纹 = self.计算指纹(标识符)
        self.标识符库[指纹] = 标识符
        self.主表[索引].init(指纹, 计数值)

    def 反馈(self, 索引, 标识符, 计数值, 序号):
        事务 = 事件()
        事务.设为事务类型()
        事务.设置索引(索引)
        事务.设置标识符(标识符)
        事务.设置计数值(计数值)
        事务.设置序号(序号)
        self.输入缓冲区.添加事务(事务)

    def 在线流长查询(self, 标识符):
        流长 = 0
        idx1, idx2, idx3, idx4 = self.计算索引(标识符)
        摘要 = self.计算摘要(标识符)
        for idx in [idx1, idx2, idx3]:
            if 标识符 == self.主表[idx].flowID:
                流长 = max(流长, self.主表[idx].count)
        if 摘要 == self.辅表[idx4].digest:
            流长 = max(流长, self.辅表[idx].count)
        return 流长

    def 下载数据平面(self):
        流集 = dict()
        for item in self.主表:
            if not item.empty():
                指纹, 计数值 = item.getRecord()
                if 指纹 in self.标识符库:
                    标识符 = self.标识符库[指纹]
                    if 标识符 not in 流集:
                        流集[标识符] = 计数值
                    else:
                        流集[标识符] += 计数值
                else:
                    pass
        return 流集

    def 计算索引(self, 标识符):
        偏移量 = 累积序列([0, self.表长1, self.表长2])
        idx1 = mytools.hash1(标识符.encode('utf-8')) % self.表长1 + 偏移量[0]
        idx2 = mytools.hash2(标识符.encode('utf-8')) % self.表长2 + 偏移量[1]
        idx3 = mytools.hash3(标识符.encode('utf-8')) % self.表长3 + 偏移量[2]
        idx4 = mytools.hash4(标识符.encode('utf-8')) % self.表长4
        return [idx1, idx2, idx3, idx4]

    def 获取带宽消耗(self):
        return self.导出数据量*8.0/(self.结束时间 - self.开始时间)

    def 备份(self, 文件名):
        备份 = dict()
        备份["小流导出次数"] = self.小流导出次数
        备份["旧流导出次数"] = self.旧流导出次数
        备份["流记录集"] = self.获取流记录集()
        备份["导出次数"] = self.导出次数
        备份["带宽消耗"] = self.导出数据量*8.0/(self.结束时间 - self.开始时间)
        备份数据(备份, 文件名)
